﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace ZHello.Utils
{
    public class DeepCloneUtil
    {

        /// <summary>
        /// 对象自身深复制
        /// 仅复制可读写的值类型和字符串
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="t"></param>
        /// <returns></returns>
        public static object DeepClone<T>(T t)
            where T : class, new()
        {
            var r = new T();
            var props = typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance);
            for (int i = 0; i < props.Length; i++)
            {
                var p = props[i];
                if (!p.CanRead || !p.CanWrite)
                {
                    continue;
                }
                if (p.PropertyType.IsValueType || p.PropertyType.IsEnum || p.PropertyType == typeof(string))
                {
                    p.SetValue(r, p.GetValue(t, null), null);
                }
                else if (p.PropertyType == typeof(Dictionary<string, string>))
                {
                    var obj = p.GetValue(t, null);
                    if (obj == null)
                    {
                        continue;
                    }
                    var dic = obj as Dictionary<string, string>;
                    if (dic.Count > 0)
                    {
                        var v = new Dictionary<string, string>();
                        foreach (var item in dic)
                        {
                            v.Add(item.Key, item.Value);
                        }
                        p.SetValue(r, v, null);
                    }
                }
            }
            return r;
        }

        /// <summary>
        /// 对象到另一个对象的深复制
        /// 仅复制可读写的值类型和字符串
        /// </summary>
        /// <typeparam name="SrcT"></typeparam>
        /// <typeparam name="TarT"></typeparam>
        /// <param name="src"></param>
        /// <param name="t"></param>
        public static void DeepCopy<SrcT, TarT>(SrcT src, out TarT t)
            where TarT : class, new()
        {
            t = new TarT();
            var props1 = typeof(SrcT).GetProperties(BindingFlags.Public | BindingFlags.Instance);
            var props2 = typeof(TarT).GetProperties(BindingFlags.Public | BindingFlags.Instance);
            var props = new List<Tuple<PropertyInfo, PropertyInfo>>();
            for (int i = 0; i < props1.Length; i++)
            {
                if (!props1[i].CanWrite || !props1[i].CanRead)
                    continue;
                for (int j = 0; j < props2.Length; j++)
                {
                    if (props2[j].CanWrite && props2[j].CanRead
                        && props1[i].Name == props2[j].Name
                        && props1[i].PropertyType == props2[j].PropertyType)
                    {
                        props.Add(new Tuple<PropertyInfo, PropertyInfo>(props1[i], props2[j]));
                    }
                }
            }
            foreach (var item in props)
            {
                var p = item.Item1;
                var p2 = item.Item2;
                if (p.PropertyType.IsValueType || p.PropertyType.IsEnum || p.PropertyType == typeof(string))
                {
                    p2.SetValue(t, p.GetValue(src, null), null);
                }
                else if (p.PropertyType == typeof(Dictionary<string, string>))
                {
                    var obj = p.GetValue(t, null);
                    if (obj == null)
                    {
                        continue;
                    }
                    var dic = obj as Dictionary<string, string>;
                    if (dic.Count > 0)
                    {
                        var v = new Dictionary<string, string>();
                        foreach (var kvp in dic)
                        {
                            v.Add(kvp.Key, kvp.Value);
                        }
                        p2.SetValue(t, v, null);
                    }
                }
            }
        }
    }
}
